﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using System.Collections;

namespace LockCrypt.AutoType {
    public static class WindowEnumerator {
        /// <summary>
        /// Window Style Flags
        /// </summary>
        [Flags]
        public enum WindowStyleFlags : uint {
            WS_OVERLAPPED = 0x00000000,
            WS_POPUP = 0x80000000,
            WS_CHILD = 0x40000000,
            WS_MINIMIZE = 0x20000000,
            WS_VISIBLE = 0x10000000,
            WS_DISABLED = 0x08000000,
            WS_CLIPSIBLINGS = 0x04000000,
            WS_CLIPCHILDREN = 0x02000000,
            WS_MAXIMIZE = 0x01000000,
            WS_BORDER = 0x00800000,
            WS_DLGFRAME = 0x00400000,
            WS_VSCROLL = 0x00200000,
            WS_HSCROLL = 0x00100000,
            WS_SYSMENU = 0x00080000,
            WS_THICKFRAME = 0x00040000,
            WS_GROUP = 0x00020000,
            WS_TABSTOP = 0x00010000,
            WS_MINIMIZEBOX = 0x00020000,
            WS_MAXIMIZEBOX = 0x00010000,
        }

        /// <summary>
        /// Extended Windows Style flags
        /// </summary>
        [Flags]
        public enum ExtendedWindowStyleFlags : int {
            WS_EX_DLGMODALFRAME = 0x00000001,
            WS_EX_NOPARENTNOTIFY = 0x00000004,
            WS_EX_TOPMOST = 0x00000008,
            WS_EX_ACCEPTFILES = 0x00000010,
            WS_EX_TRANSPARENT = 0x00000020,

            WS_EX_MDICHILD = 0x00000040,
            WS_EX_TOOLWINDOW = 0x00000080,
            WS_EX_WINDOWEDGE = 0x00000100,
            WS_EX_CLIENTEDGE = 0x00000200,
            WS_EX_CONTEXTHELP = 0x00000400,

            WS_EX_RIGHT = 0x00001000,
            WS_EX_LEFT = 0x00000000,
            WS_EX_RTLREADING = 0x00002000,
            WS_EX_LTRREADING = 0x00000000,
            WS_EX_LEFTSCROLLBAR = 0x00004000,
            WS_EX_RIGHTSCROLLBAR = 0x00000000,

            WS_EX_CONTROLPARENT = 0x00010000,
            WS_EX_STATICEDGE = 0x00020000,
            WS_EX_APPWINDOW = 0x00040000,

            WS_EX_LAYERED = 0x00080000,

            WS_EX_NOINHERITLAYOUT = 0x00100000, // Disable inheritence of mirroring by children
            WS_EX_LAYOUTRTL = 0x00400000, // Right to left mirroring

            WS_EX_COMPOSITED = 0x02000000,
            WS_EX_NOACTIVATE = 0x08000000
        }


        #region EnumWindows
        /// <summary>
        /// EnumWindows wrapper for .NET
        /// </summary>
        public class EnumWindows {
            #region Delegates
            private delegate int EnumWindowsProc(IntPtr hwnd, int lParam);
            #endregion

            #region UnManagedMethods
            private class UnManagedMethods {
                [DllImport("user32")]
                public extern static int EnumWindows(EnumWindowsProc lpEnumFunc, int lParam);
                [DllImport("user32")]
                public extern static int EnumChildWindows(IntPtr hWndParent, EnumWindowsProc lpEnumFunc, int lParam);
            }
            #endregion

            #region Member Variables
            private EnumWindowsCollection items = null;
            #endregion

            /// <summary>
            /// Returns the collection of windows returned by
            /// GetWindows
            /// </summary>
            public EnumWindowsCollection Items {
                get {
                    return this.items;
                }
            }

            /// <summary>
            /// Gets all top level windows on the system.
            /// </summary>
            public void GetWindows() {
                this.items = new EnumWindowsCollection();
                UnManagedMethods.EnumWindows(
                    new EnumWindowsProc(this.WindowEnum),
                    0);
            }
            /// <summary>
            /// Gets all child windows of the specified window
            /// </summary>
            /// <param name="hWndParent">Window Handle to get children for</param>
            public void GetWindows(
                IntPtr hWndParent) {
                this.items = new EnumWindowsCollection();
                UnManagedMethods.EnumChildWindows(
                    hWndParent,
                    new EnumWindowsProc(this.WindowEnum),
                    0);
            }

            #region EnumWindows callback
            /// <summary>
            /// The enum Windows callback.
            /// </summary>
            /// <param name="hWnd">Window Handle</param>
            /// <param name="lParam">Application defined value</param>
            /// <returns>1 to continue enumeration, 0 to stop</returns>
            private int WindowEnum(
                IntPtr hWnd,
                int lParam) {
                if(this.OnWindowEnum(hWnd)) {
                    return 1;
                } else {
                    return 0;
                }
            }
            #endregion

            /// <summary>
            /// Called whenever a new window is about to be added
            /// by the Window enumeration called from GetWindows.
            /// If overriding this function, return true to continue
            /// enumeration or false to stop.  If you do not call
            /// the base implementation the Items collection will
            /// be empty.
            /// </summary>
            /// <param name="hWnd">Window handle to add</param>
            /// <returns>True to continue enumeration, False to stop</returns>
            protected virtual bool OnWindowEnum(
                IntPtr hWnd) {
                items.Add(hWnd);
                return true;
            }

            #region Constructor, Dispose
            public EnumWindows() {
                // nothing to do
            }
            #endregion
        }
        #endregion EnumWindows

        #region EnumWindowsCollection
        /// <summary>
        /// Holds a collection of Windows returned by GetWindows.
        /// </summary>
        public class EnumWindowsCollection : ReadOnlyCollectionBase {
            /// <summary>
            /// Add a new Window to the collection.  Intended for
            /// internal use by EnumWindows only.
            /// </summary>
            /// <param name="hWnd">Window handle to add</param>
            public void Add(IntPtr hWnd) {
                EnumWindowsItem item = new EnumWindowsItem(hWnd);
                this.InnerList.Add(item);
            }

            /// <summary>
            /// Gets the Window at the specified index
            /// </summary>
            public EnumWindowsItem this[int index] {
                get {
                    return (EnumWindowsItem)this.InnerList[index];
                }
            }

            /// <summary>
            /// Constructs a new EnumWindowsCollection object.
            /// </summary>
            public EnumWindowsCollection() {
                // nothing to do
            }
        }
        #endregion

        #region EnumWindowsItem
        /// <summary>
        /// Provides details about a Window returned by the 
        /// enumeration
        /// </summary>
        public class EnumWindowsItem {
            #region Structures
            [StructLayout(LayoutKind.Sequential, Pack = 4)]
            private struct RECT {
                public int Left;
                public int Top;
                public int Right;
                public int Bottom;
            }
            [StructLayout(LayoutKind.Sequential, Pack = 4)]
            private struct FLASHWINFO {
                public int cbSize;
                public IntPtr hwnd;
                public int dwFlags;
                public int uCount;
                public int dwTimeout;
            }
            #endregion

            #region UnManagedMethods
            private class UnManagedMethods {
                [DllImport("user32")]
                public extern static int IsWindowVisible(IntPtr hWnd);
                [DllImport("user32", CharSet = CharSet.Auto)]
                public extern static int GetWindowText( IntPtr hWnd, StringBuilder lpString, int cch);
                [DllImport("user32", CharSet = CharSet.Auto)]
                public extern static int GetWindowTextLength(IntPtr hWnd);
                [DllImport("user32")]
                public extern static int BringWindowToTop(IntPtr hWnd);
                [DllImport("user32")]
                public extern static int SetForegroundWindow(IntPtr hWnd);
                [DllImport("user32")]
                public extern static int IsIconic(IntPtr hWnd);
                [DllImport("user32")]
                public extern static int IsZoomed(IntPtr hwnd);
                [DllImport("user32", CharSet = CharSet.Auto)]
                public extern static int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount);
                [DllImport("user32")]
                public extern static int FlashWindow(IntPtr hWnd, ref FLASHWINFO pwfi);
                [DllImport("user32")]
                public extern static int GetWindowRect(IntPtr hWnd, ref RECT lpRect);
                [DllImport("user32", CharSet = CharSet.Auto)]
                public extern static int SendMessage(IntPtr hWnd, int wMsg, IntPtr wParam, IntPtr lParam);
                [DllImport("user32", CharSet = CharSet.Auto)]
                public extern static uint GetWindowLong(IntPtr hwnd, int nIndex);
                public const int WM_COMMAND = 0x111;
                public const int WM_SYSCOMMAND = 0x112;

                public const int SC_RESTORE = 0xF120;
                public const int SC_CLOSE = 0xF060;
                public const int SC_MAXIMIZE = 0xF030;
                public const int SC_MINIMIZE = 0xF020;

                public const int GWL_STYLE = (-16);
                public const int GWL_EXSTYLE = (-20);

                /// <summary>
                /// Stop flashing. The system restores the window to its original state.
                /// </summary>
                public const int FLASHW_STOP = 0;
                /// <summary>
                /// Flash the window caption. 
                /// </summary>
                public const int FLASHW_CAPTION = 0x00000001;
                /// <summary>
                /// Flash the taskbar button.
                /// </summary>
                public const int FLASHW_TRAY = 0x00000002;
                /// <summary>
                /// Flash both the window caption and taskbar button.
                /// </summary>
                public const int FLASHW_ALL = (FLASHW_CAPTION | FLASHW_TRAY);
                /// <summary>
                /// Flash continuously, until the FLASHW_STOP flag is set.
                /// </summary>
                public const int FLASHW_TIMER = 0x00000004;
                /// <summary>
                /// Flash continuously until the window comes to the foreground. 
                /// </summary>
                public const int FLASHW_TIMERNOFG = 0x0000000C;
            }
            #endregion

            /// <summary>
            /// The window handle.
            /// </summary>
            private IntPtr hWnd = IntPtr.Zero;

            /// <summary>
            /// To allow items to be compared, the hash code
            /// is set to the Window handle, so two EnumWindowsItem
            /// objects for the same Window will be equal.
            /// </summary>
            /// <returns>The Window Handle for this window</returns>
            public override System.Int32 GetHashCode() {
                return (System.Int32)this.hWnd;
            }

            /// <summary>
            /// Gets the window's handle
            /// </summary>
            public IntPtr Handle {
                get {
                    return this.hWnd;
                }
            }

            /// <summary>
            /// Gets the window's title (caption)
            /// </summary>
            public string Text {
                get {
                    StringBuilder title = new StringBuilder(260, 260);
                    UnManagedMethods.GetWindowText(this.hWnd, title, title.Capacity);
                    return title.ToString();
                }
            }

            /// <summary>
            /// Gets the window's class name.
            /// </summary>
            public string ClassName {
                get {
                    StringBuilder className = new StringBuilder(260, 260);
                    UnManagedMethods.GetClassName(this.hWnd, className, className.Capacity);
                    return className.ToString();
                }
            }

            /// <summary>
            /// Gets/Sets whether the window is iconic (mimimised) or not.
            /// </summary>
            public bool Iconic {
                get {
                    return ((UnManagedMethods.IsIconic(this.hWnd) == 0) ? false : true);
                }
                set {
                    UnManagedMethods.SendMessage(
                        this.hWnd,
                        UnManagedMethods.WM_SYSCOMMAND,
                        (IntPtr)UnManagedMethods.SC_MINIMIZE,
                        IntPtr.Zero);
                }
            }

            /// <summary>
            /// Gets/Sets whether the window is maximised or not.
            /// </summary>
            public bool Maximised {
                get {
                    return ((UnManagedMethods.IsZoomed(this.hWnd) == 0) ? false : true);
                }
                set {
                    UnManagedMethods.SendMessage(
                        this.hWnd,
                        UnManagedMethods.WM_SYSCOMMAND,
                        (IntPtr)UnManagedMethods.SC_MAXIMIZE,
                        IntPtr.Zero);
                }
            }

            /// <summary>
            /// Gets whether the window is visible.
            /// </summary>
            public bool Visible {
                get {
                    return ((UnManagedMethods.IsWindowVisible(this.hWnd) == 0) ? false : true);
                }
            }

            /// <summary>
            /// Gets the bounding rectangle of the window
            /// </summary>
            public System.Drawing.Rectangle Rect {
                get {
                    RECT rc = new RECT();
                    UnManagedMethods.GetWindowRect(
                        this.hWnd,
                        ref rc);
                    System.Drawing.Rectangle rcRet = new System.Drawing.Rectangle(
                        rc.Left, rc.Top,
                        rc.Right - rc.Left, rc.Bottom - rc.Top);
                    return rcRet;
                }
            }

            /// <summary>
            /// Gets the location of the window relative to the screen.
            /// </summary>
            public System.Drawing.Point Location {
                get {
                    System.Drawing.Rectangle rc = Rect;
                    System.Drawing.Point pt = new System.Drawing.Point(
                        rc.Left,
                        rc.Top);
                    return pt;
                }
            }

            /// <summary>
            /// Gets the size of the window.
            /// </summary>
            public System.Drawing.Size Size {
                get {
                    System.Drawing.Rectangle rc = Rect;
                    System.Drawing.Size sz = new System.Drawing.Size(
                        rc.Right - rc.Left,
                        rc.Bottom - rc.Top);
                    return sz;
                }
            }

            /// <summary>
            /// Restores and Brings the window to the front, 
            /// assuming it is a visible application window.
            /// </summary>
            public void Restore() {
                if(Iconic) {
                    UnManagedMethods.SendMessage(
                        this.hWnd,
                        UnManagedMethods.WM_SYSCOMMAND,
                        (IntPtr)UnManagedMethods.SC_RESTORE,
                        IntPtr.Zero);
                }
                UnManagedMethods.BringWindowToTop(this.hWnd);
                UnManagedMethods.SetForegroundWindow(this.hWnd);
            }

            public WindowStyleFlags WindowStyle {
                get {
                    return (WindowStyleFlags)UnManagedMethods.GetWindowLong(
                        this.hWnd, UnManagedMethods.GWL_STYLE);
                }
            }

            public ExtendedWindowStyleFlags ExtendedWindowStyle {
                get {
                    return (ExtendedWindowStyleFlags)UnManagedMethods.GetWindowLong(
                        this.hWnd, UnManagedMethods.GWL_EXSTYLE);
                }
            }

            /// <summary>
            ///  Constructs a new instance of this class for
            ///  the specified Window Handle.
            /// </summary>
            /// <param name="hWnd">The Window Handle</param>
            public EnumWindowsItem(IntPtr hWnd) {
                this.hWnd = hWnd;
            }
        }
        #endregion
    }
}